package cn.itzzx.lagou.minicat.server;

import cn.itzzx.lagou.minicat.classload.MyClassLoader;
import cn.itzzx.lagou.minicat.mapper.Mapper;
import cn.itzzx.lagou.minicat.request.RequestProcessor;
import cn.itzzx.lagou.minicat.servlet.HttpServlet;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 *
 * @author Martin
 * @since 1.0
 */
public class Bootstrap {

    private Mapper mapper = new Mapper();

    private Map<String, HttpServlet> servletMap = new HashMap<>();
    /**
     * 定义socket监听的端口 默认8080
     */
    private int port = 8080;


    /**
     * Minicat 启动需要初始化展开的一些操作
     */
    public void start() throws Exception {
        //定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;

        long keepAliveTime = 100L;

        TimeUnit unit = TimeUnit.SECONDS;

        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Minicat start on port : " + port);

        while (true) {
            Socket accept = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(accept, mapper);
            threadPoolExecutor.execute(requestProcessor);
        }
    }



    public void init() {
        //加载 server.xml 配置文件
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        //通过 dom4j 解析 server.xml 配置文件
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            //获取根标签
            Element rootElement = document.getRootElement();
            Element service = (Element) rootElement.selectSingleNode("Service");
            Element connector = (Element) service.selectSingleNode("Connector");
            //获取端口并设置监听端口
            String port = connector.attributeValue("port");
            this.port = Integer.valueOf(port);

            Element engine = (Element) service.selectSingleNode("Engine");
            //获取Host
            List<Element> hostElements = engine.selectNodes("Host");
            hostElements.forEach(hostElement -> {
                try {
                    Mapper.MappedHost mappedHost = loadHost(hostElement);
                    mapper.addHost(mappedHost);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }


    private Mapper.MappedHost loadHost(Element hostElement) throws Exception {
        String hostname = hostElement.attributeValue("name");
        String appBase = hostElement.attributeValue("appBase");
        Mapper.MappedHost mappedHost = mapper.new MappedHost(hostname,appBase);
        //appBase使用绝对路径
        File fileDir = new File(appBase);
        if(!fileDir.exists()){
            throw new Exception(appBase + "文件不存在");
        }

        File[] files = fileDir.listFiles();

        Map<String,Mapper.MappedContext> mappedContexts = new HashMap<>();
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            //判断是否是一个文件夹
            if(file.isDirectory()){
                String path = file.getAbsolutePath();
                String appName = file.getName();
                mappedContexts.put(appName,loadContext(path,appName));
            }
        }
        mappedHost.setContextMap(mappedContexts);
        return mappedHost;
    }

    private Mapper.MappedContext loadContext(String path, String appName) throws FileNotFoundException {
        Mapper.MappedContext mappedContext = mapper.new MappedContext();
        mappedContext.setName(appName);
        File file = new File(path + "/web.xml");
        if(!file.exists()){
            //不存在web.xml 直接返回
            return mappedContext;
        }
        FileInputStream resourceAsStream = new FileInputStream(path+"/web.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document read = saxReader.read(resourceAsStream);

            //获取根元素
            Element rootElement = read.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");

            for (int i = 0; i < selectNodes.size(); i++) {
                Element element = selectNodes.get(i);

                //<servlet-name>lagou</servlet-name>
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();

                //<servlet-class>cn.itzzx.lagou.minicat.servlet.LagouServlet</servlet-class>
                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();

                //根据 servlet-name 的值去找到 url-patten (获取 servlet-name == servletName 的 url-patten
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                MyClassLoader myClassLoader = new MyClassLoader(path);
                Class<?> aClass = myClassLoader.findClass(servletClass);
                HttpServlet httpServlet = (HttpServlet)aClass.getDeclaredConstructor().newInstance();
                //将servlet存储到 context下的 wrapper集合中  web.xml 配置的uri 与 servlet做映射
                mappedContext.getWrapper().put(urlPattern,httpServlet);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return mappedContext;
    }


    /**
     * Minicat的启动入口
     *
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.init();
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
